/*******************************************************************************
 * Copyright (c) 2011 TXT e-solutions SpA
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the research area ICT-2009.1.3
 * Internet of Things and enterprise environments.
 *
 *
 * Authors:
 *     Cristoforo Seccia (TXT e-solutions SpA)
 *
 * Contributors:
 *      Domenico Rotondi (TXT e-solutions SpA)
 *******************************************************************************/
package it.txt.access.capability.demo.schema.request.factory;

import it.txt.access.capability.commons.identifier.IdentifierGenerator;
import it.txt.access.capability.commons.schema.factory.CapabilitySchemasJaxbFaxtory;
import it.txt.access.capability.commons.schema.factory.CapabilitySchemasLoader;
import it.txt.access.capability.commons.utils.CalendarUtils;
import it.txt.access.capability.commons.utils.DOMUtils;
import it.txt.access.capability.demo.schema.CapabilityRequestType;
import it.txt.access.capability.demo.schema.ObjectFactory;
import it.txt.access.capability.demo.schema.factory.CapabilityDemoFactoryException;
import it.txt.access.capability.schema.AccessRightsCapabilityType;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 *
 * @author Cristoforo Seccia (TXT e-solutions SpA)
 */
public class CapabilityDemoRequestFactory {

    private static final Logger logger = Logger.getLogger(CapabilityDemoRequestFactory.class.getName());
    public static final String JAXB_CONTEXT = "it.txt.access.capability.demo.schema";
    public static final String SIGN_DOCUMENT_AFTER_NODE = "CapabilityRequestID";
    public static final int CAPABILITY_REQUEST_VERSION = 1;
    private static CapabilityDemoRequestFactory factory;
    private final CapabilitySchemasJaxbFaxtory wrapper;
    private final ObjectFactory requestFactory;


    private CapabilityDemoRequestFactory(String capabilitySchemasRootPath) throws CapabilityDemoFactoryException{
        try {
            CapabilitySchemasLoader loader = new CapabilitySchemasLoader(capabilitySchemasRootPath);
            wrapper = new CapabilitySchemasJaxbFaxtory(
                    loader.loadDemoCapabilityEntities(capabilitySchemasRootPath),
                    CapabilityRequestType.class);
            requestFactory = new ObjectFactory();
        } catch (JAXBException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        } catch (SAXException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized static CapabilityDemoRequestFactory getInstance(String capabilitySchemasRootPath) throws CapabilityDemoFactoryException {
        if(factory == null){
            factory = new CapabilityDemoRequestFactory(capabilitySchemasRootPath);
        }
        return factory;
    }

    /**
     * Warning! Use this method only if the CapabilitySchemaFactory has been
     * initialized with one of the parameterized 'getInstance(...)' method.
     */
    public synchronized static CapabilityDemoRequestFactory getInstance() throws CapabilityDemoFactoryException {
        if(factory == null){
            throw new CapabilityDemoFactoryException("CapabilityDemoRequestFactory has not been initialized");
        }
        return factory;
    }


    public synchronized CapabilityRequestType createRequest(Source source) throws CapabilityDemoFactoryException {
        try {
            logger.log(Level.INFO, "Calling getCapabilityRequestFromSource()");
            return (CapabilityRequestType) wrapper.getUnmarshaller().unmarshal(source);
        } catch (JAXBException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized CapabilityRequestType createRequest(InputStream inputStream) throws CapabilityDemoFactoryException {
        try {
            logger.log(Level.INFO, "Calling getCapabilityRequestFromInputStream()");
            return (CapabilityRequestType) wrapper.getUnmarshaller().unmarshal(inputStream);
        } catch (JAXBException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized Document createRequestDocument(CapabilityRequestType request) throws CapabilityDemoFactoryException {
        logger.log(Level.INFO, "Calling getCapabilityRequestToDocument() on RequestID: {0}",
                request.getCapabilityRequestID());
        return createRequest(request, Boolean.FALSE);
    }

    public synchronized Document createFragmentRequestDocument(CapabilityRequestType request) throws CapabilityDemoFactoryException {
        logger.log(Level.INFO, "Calling getFragmentAccessRightsCapabilityToDocument() on RequestID: {0}",
                request.getCapabilityRequestID());
        return createRequest(request, Boolean.TRUE);
    }

    private synchronized Document createRequest(CapabilityRequestType request, boolean fragment) throws CapabilityDemoFactoryException {
        try {
            Document capabilityDocument = DOMUtils.newDocument();
            wrapper.getMarshaller().setProperty(Marshaller.JAXB_FRAGMENT, fragment);
            wrapper.getMarshaller().marshal(request, capabilityDocument);
            return capabilityDocument;
        } catch (ParserConfigurationException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        } catch (JAXBException ex) {
            throw new CapabilityDemoFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized CapabilityRequestType createRequest(String operation, String resourceID,
            AccessRightsCapabilityType capability) throws CapabilityDemoFactoryException {

        logger.log(Level.INFO, "Calling createCapabilityRequest() ");
        //Create a new Capability Request Object Factory
        
        CapabilityRequestType request = requestFactory.createCapabilityRequestType();
        //Set the version
        request.setVersion(String.valueOf(CAPABILITY_REQUEST_VERSION));
        //Set the request istant
        Date now = new Date();
        XMLGregorianCalendar issueIstant = null;
        try {
            issueIstant = CalendarUtils.newXmlGregorianCalendarFromDateAndTime(now, now);
        } catch (DatatypeConfigurationException ex) {
            throw new CapabilityDemoFactoryException("Error while creating Issue istant Date.", ex);
        }
        XMLGregorianCalendar issueIstantUTC = issueIstant.normalize();
        request.setRequestIstant(issueIstantUTC);
        //Set the Capability Request ID
        try {
            request.setCapabilityRequestID(
                    IdentifierGenerator.generateIdentifier());
        } catch (NoSuchAlgorithmException ex) {
            throw new CapabilityDemoFactoryException("Error while generating identifier.", ex);
        }
        //Set the operation
        request.setOperation(operation);
        //Set the resource id
        request.setResourceID(resourceID);
        //Set the capability
        CapabilityRequestType.RequestAccessRightsCapability requestCapability =
                requestFactory.createCapabilityRequestTypeRequestAccessRightsCapability();
        requestCapability.setAccessRightsCapability(capability);
        request.setRequestAccessRightsCapability(requestCapability);
        //return the result
        return request;
    }
}
